package org.monster.context.core.dispatcher;

import org.dom4j.DocumentException;
import org.monster.context.core.ApplicationClassLoaderImpl;
import org.monster.context.core.ApplicationContext;
import org.monster.context.core.FilterConfigCache;
import org.monster.context.core.filter.ApplicationFilterChain;
import org.monster.context.core.headerfilter.*;
import org.monster.context.core.parameter.HttpResponse;
import org.monster.context.core.request.HttpRequest;
import org.monster.context.reader.tag.FilterTag;
import org.monster.context.servlet.ServletContextConfig;
import org.monster.context.servlet.ServletExecutor;

import javax.servlet.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 应用过滤器转发
 *
 * @author Monster
 */
public class ApplicationFilterDispatcher implements Filter {

    /**
     * 过滤器配置键值
     */
    private static final FilterConfigCache FILTER_CONFIG_CACHE = FilterConfigCache.getFilterConfigCache();
    /**
     * 应用上下文
     */
    private final ApplicationContext applicationContext = new ApplicationContext(
            new ServletExecutor(), new ServletContextConfig(),
            new ApplicationClassLoaderImpl());
    /**
     * 请求
     */
    private final HttpRequest request;
    /**
     * 响应
     */
    private final HttpResponse response;

    /**
     * 构造方法
     *
     * @param request  请求
     * @param response 响应
     */
    public ApplicationFilterDispatcher(HttpRequest request, HttpResponse response) {
        this.request = request;
        this.response = response;

    }

    /**
     * 转发过滤器
     *
     * @param request  请求
     * @param response 响应
     * @param chain    通道
     * @throws ServletException 服务者异常
     * @throws IOException      输入输出异常
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        String forwardURI = FILTER_CONFIG_CACHE.getForwardURI();
        if (forwardURI.indexOf("/") == 0) {
            forwardURI = forwardURI.substring(1, forwardURI.length());
        }
        this.request.setRequestURI(forwardURI);
        this.request.setDispatcherType(DispatcherType.FORWARD);
        ApplicationFilterChain.setRequest(this.request);
        ApplicationFilterChain.setResponse(this.response);
        ApplicationFilterChain applicationFilterChain = (ApplicationFilterChain) chain;
        ApplicationFilterChain.dispatch = true;
        List<Filter> filters = getFilters();
        applicationFilterChain.addFilters(filters);
        applicationFilterChain.doFilter(request, response);
        try {
            applicationContext.service(this.request, this.response);
        } catch (DocumentException | ClassNotFoundException ex) {
            Logger.getLogger(ApplicationFilterDispatcher.class.getName()).log(
                    Level.SEVERE, null, ex);
        }
    }

    /**
     * 初始化
     *
     * @param arg0 过滤器配置
     * @throws ServletException 服务者异常
     */
    @Override
    public void init(FilterConfig arg0) throws ServletException {

    }

    /**
     * 销毁
     */
    @Override
    public void destroy() {
    }

    /**
     * 获取 过滤器 集合
     *
     * @return 过滤器 集合
     */
    private List<Filter> getFilters() {
        List<Filter> filters = new ArrayList<>();
        List<FilterTag> tags = FILTER_CONFIG_CACHE.getTag(request.getContextPath());
        if (tags != null) {
            for (FilterTag filterTag : tags) {
                if (filterTag.getMappingFilterUrlPattern().equals(
                        request.getServletPath())) {
                    String className = filterTag.getFilterClass();
                    Filter filter = FILTER_CONFIG_CACHE.getFilter(request
                            .getContextPath() + "_" + className);
                    filters.add(filter);
                }
            }
            filters.add(new HeaderInfoChangeFilter());
            filters.add(new HeaderPathFilter());
            filters.add(new HeaderURLFilter());
            filters.add(new HeaderResponseProtocolFilter());
            filters.add(new HeaderMimeFilter());
        }
        return filters;
    }

}
